﻿using System.Collections.Generic;
using System.Linq;
using System;
using System.ComponentModel;

namespace Device
{
    public static class DataPackUtils
    {
        /// <summary>
        /// CRC16Modbus校验算法验证数据包
        /// </summary>
        /// <param name="dataPack"></param>
        /// <returns></returns>
        public static bool ValidatePackageCRC16Modbus(byte[] dataPack)
        {
            if (dataPack == null || dataPack.Length < 2)
            {
                return false;
            }
            var checkSum = dataPack.TakeLast(2);
            var adu = dataPack.Take(dataPack.Length - 2);
            var crc = CRC16_modbus_utils.CRC16_modbus(adu.ToArray());
            var bCrc = StringHexByteUtils.Uint16ToBytes(crc).Reverse().ToArray();
            var result = true;
            if (bCrc.Length == checkSum.Count())
            {
                for (int i = 0; i < bCrc.Length; i++)
                {
                    if (bCrc[i] != checkSum.ElementAt(i))
                    {
                        result = false;
                        break;
                    }
                }
            }
            return result;
        }
    }
    /// <summary>
    /// 标签行为信息
    /// </summary>

    public class TagBehavior
    {
        public string TagId { get; set; }
        public int Behavior { get; set; }
        public string Msg { get; set; } = "SOS";
    }

    public enum TagBehaviorType
    {

        SOS = 1,
        TextMsg = 2,
    }
    public class AnchorOtaStep
    {

        public string AnchorId { get; set; }
        public OtaStep Step { get; set; }

        public StepResult Result { get; set; }


    }

    public enum OtaStep
    {
        Downloading,
        Installing,
        Complete
    }

    public enum StepResult
    {
        Success,
        Failure
    }
    public class TagRegInfo
    {
        public string AnchorId { get; set; }

        public string TagId { get; set; }

    }    /// <summary>
         /// Tlv协议内容
         /// </summary>
    public class TlvBody
    {
        public byte type { get; set; }

        public byte length { get; set; }

        public byte[] value { get; set; }
    }
    public enum FunctionCode
    {
        [Description("回复建立连接")]
        Connect = 0x02,
        [Description("回复心跳回复")]
        HeartBeat = 0x04,
        [Description("回复Server端读取Anchor")]
        ReadAnchor = 0x12,
        [Description("回复Server端设置Anchor")]
        SettingAnchor = 0x14,
        [Description("回复Server端读取Tag")]
        ReadTag = 0x16,
        [Description("回复Server端设置Tag")]
        SettingTag = 0x18
    }
    public static class ServerProtocolDecode
    {
        /// <summary>
        /// 通过functioncode 返回function类型
        /// </summary>
        /// <param name="code"></param>
        /// <returns>Functioncode的枚举值</returns>
        public static FunctionCode GetFunction(byte code)
        {
            return (FunctionCode)code;
        }

        /// <summary>
        /// 获取数据长度字节数组
        /// </summary>
        /// <param name="data">数据包</param>
        /// <returns>长度的字节数组</returns>
        public static byte[] GetLength(byte[] data)
        {
            if (data != null && data.Length > 4)
                return data.Skip(2).Take(2).ToArray();
            else
                return null;
        }

        /// <summary>
        /// 解析数据包中的ServerID
        /// </summary>
        /// <param name="data">数据包</param>
        /// <returns>Server ID 字节数组</returns>
        public static byte[] GetServerId(byte[] data)
        {
            if (data != null && data.Length > 8)
                return data.Skip(4).Take(4).ToArray();
            else
                return null;
        }

        /// <summary>
        /// 解析数据包里的TLV参数列表
        /// </summary>
        /// <param name="data">数据包</param>
        /// <returns>TLV 参数列表</returns>
        public static List<Tlv> GetTlvList_New(byte[] data)
        {
            if (data == null || data.Length == 0)
            {
                return null;
            }
            List<Tlv> list = new List<Tlv>();
            int cursor = 0;
            while (cursor < data.Length)
            {
                Tlv tlv = new Tlv();
                tlv.type = data.Skip(cursor).Take(1).SingleOrDefault();
                cursor += 1;
                tlv.length = data.Skip(cursor).Take(1).FirstOrDefault();
                cursor++;
                var iLen = Convert.ToInt32(tlv.length);
                tlv.value = data.Skip(cursor).Take(iLen).ToArray();
                cursor += iLen;
                list.Add(tlv);
            }
            return list;
        }

        /// <summary>
        /// 解析数据包里的基站编号
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static byte[] GetAnchorId(byte[] data)
        {
            if (data != null && data.Length > 12)
                return data.Skip(6).Take(4).ToArray();
            else
                return null;
        }

        public static byte[] GetReserved1(byte[] data)
        {
            if (data != null && data.Length > 13)
                return data.Skip(12).Take(1).ToArray();
            else
                return null;
        }

        public static byte[] GetReserved2(byte[] data)
        {

            if (data != null && data.Length > 14)
                return data.Skip(13).Take(1).ToArray();
            else
                return null;
        }

        public static IEnumerable<TlvBody> GetPayload(byte[] data)
        {
            if (data == null)
            {
                return null;
            }

            var bLen = data.Skip(10).Take(2).ToArray();


            int a = BitConverter.ToInt16(bLen, 0);

            var byteData = data.Skip(12).Take(a);


            if (!byteData.Any())
            {
                return null;
            }
            return GetTlvList(byteData.ToArray());
        }

        static int tlvLen = 0;
        static int cur = 0;
        public static IEnumerable<TlvBody> GetTlvList(byte[] payloaddata)
        {
            if (payloaddata == null || payloaddata.Length == 0)
            {
                return null;
            }
            string strPayloaddata = StringHexByteUtils.ByteToHexString(payloaddata);
            var payLoadDataStrArray = strPayloaddata.Split(" ");
            List<TlvBody> list = new List<TlvBody>();
            int cursor = 0;
            while (cursor < payloaddata.Length)
            {
                TlvBody tlv = new TlvBody();
                tlv.type = payloaddata.Skip(cursor).Take(1).SingleOrDefault();
                cursor += 1;
                tlv.length = payloaddata.Skip(cursor).Take(1).FirstOrDefault();
                cursor++;
                var iLen = Convert.ToInt32(tlv.length);
                tlv.value = payloaddata.Skip(cursor).Take(iLen).ToArray();
                cursor += iLen;
                list.Add(tlv);
            }
            return list;
        }
        public static string ConvertToIpString(byte[] value, string res)
        {
            foreach (var bit in value)
            {
                var a = ((int)bit).ToString();
                res += a + ".";
            }
            if (res != null && res.EndsWith("."))
            {
                res = res.Substring(0, res.Length - 1);
            }

            return res;
        }

        public static TagRegInfo TagRegReq(byte[] data)
        {
            TagRegInfo tagRegInfo = null;
            try
            {
                if (!DataPackUtils.ValidatePackageCRC16Modbus(data))
                {
                    return null;
                }
                var bLen = data.Skip(10).Take(2).ToArray();
                int a = BitConverter.ToInt16(bLen, 0);
                var list = GetTlvList_New(data.Skip(12).Take(a).ToArray());
                tagRegInfo = new TagRegInfo
                {
                    AnchorId = StringHexByteUtils.ByteToHexString(data.Skip(6).Take(4).ToArray()).Replace(" ", ""),
                    TagId = StringHexByteUtils.ByteToHexString((list.Where(o => o.type == 0x01).FirstOrDefault().value)).Replace(" ", ""),
                };
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return tagRegInfo;
        }


        public static TagBehavior DescryptTagSOS(byte[] data)
        {
            TagBehavior tagSos = null;
            if (!DataPackUtils.ValidatePackageCRC16Modbus(data))
            {
                return tagSos;
            }
            try
            {
                var bLen = data.Skip(10).Take(2).ToArray();
                int a = BitConverter.ToInt16(bLen, 0);
                var list = GetTlvList_New(data.Skip(12).Take(a).ToArray());
                if (list.Count > 0 && list.Any(o => o.type == 0x01))
                {
                    tagSos = new TagBehavior();
                    tagSos.TagId = BitConverter.ToString(list.Single<Tlv>(o => o.type == 0x01).value).Replace("-", "");
                    tagSos.Behavior = (int)TagBehaviorType.SOS;
                    tagSos.Msg = "";
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.StackTrace);
            }
            return tagSos;
        }

        /// <summary>
        /// 解析OTA进度
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static AnchorOtaStep DescryptAnchorOTAStep(byte[] data)
        {
            AnchorOtaStep anchorOtaStep = null;
            if (DataPackUtils.ValidatePackageCRC16Modbus(data))
            {
                anchorOtaStep = new AnchorOtaStep();
                anchorOtaStep.AnchorId = BitConverter.ToString(GetAnchorId(data)).Replace("-", "");
                var tlvs = GetPayload(data).ToArray();
                if (tlvs.Count() > 0)
                {
                    switch (tlvs[0].type)
                    {
                        case 0x01:
                            anchorOtaStep.Step = OtaStep.Downloading;
                            break;
                        case 0x02:
                            anchorOtaStep.Step = OtaStep.Installing;
                            break;
                        case 0x03:
                            anchorOtaStep.Step = OtaStep.Complete;
                            break;
                    }
                    if (tlvs[0].value[0] == 0x01)
                    {
                        anchorOtaStep.Result = StepResult.Success;
                    }
                    else
                    {
                        anchorOtaStep.Result = StepResult.Failure;
                    }
                }

            }
            return anchorOtaStep;
        }
    }

}
